package cn.stylefeng.guns.cloud.workflow.modular.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.guns.cloud.libs.context.auth.LoginContext;
import cn.stylefeng.guns.cloud.libs.mp.page.PageFactory;
import cn.stylefeng.guns.cloud.model.page.PageResult;
import cn.stylefeng.guns.cloud.system.api.model.UserInfo;
import cn.stylefeng.guns.cloud.workflow.modular.model.result.ApproveHistoryResult;
import cn.stylefeng.guns.cloud.workflow.modular.service.ActBaseService;
import cn.stylefeng.guns.cloud.workflow.modular.service.ApproveHistoryService;
import cn.stylefeng.guns.cloud.workflow.modular.service.CommonTaskService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.*;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.form.StartFormData;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.task.TaskDefinition;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.*;

/**
 * 通用任务 服务实现类
 *
 * @Author xuyuxiang
 * @Date 2019/11/14 15:25
 **/
@Service
public class CommonTaskServiceImpl implements CommonTaskService {

    private final String BASE64_PREFIX = "data:image/png;base64,";

    private final String APPLY_USER_ID = "applyUserId";

    @Autowired
    private TaskService taskService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private FormService formService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private ApproveHistoryService approveHistoryService;

    @Autowired
    private ProcessEngineConfiguration processEngineConfiguration;

    @Autowired
    private ActBaseService actBaseService;


    /**
     * 查看任务详情
     *
     * @Author xuyuxiang
     * @Date 2019/11/14 15:28
     **/
    @Override
    public Map<String, Object> viewTaskDetail(String processInstanceId) {
        //查看任务详情，使用流程定义实例id查看，返回数据包含表单地址，
        // 是否需要审批（flag,用于展示或隐藏审批框),审批意见记录，流程图
        Map<String, Object> resultMap = new HashMap<>();
        //获取历史流程实例
        HistoricProcessInstance processInstance = historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        //获取流程定义实例
        String processDefinitionId = processInstance.getProcessDefinitionId();
        ProcessDefinition processDefinition = repositoryService.
                createProcessDefinitionQuery().
                processDefinitionId(processDefinitionId).
                singleResult();
        String formKey = "";
        //获取businessKey
        HistoricProcessInstance historicProcessInstance = historyService.
                createHistoricProcessInstanceQuery().
                processInstanceId(processInstanceId).singleResult();
        String businessKey = historicProcessInstance.getBusinessKey();

        //判断是否有启动表单
        boolean hasStartFormKey = processDefinition.hasStartFormKey();
        //有的话则取表单key
        if (hasStartFormKey) {
            //formKey + businessKey作为表单地址
            StartFormData startFormData = formService.getStartFormData(processDefinitionId);
            formKey = startFormData.getFormKey();
            formKey = formKey + businessKey;
        }

        //根据processInstanceId获取流程，如果流程未结束，且当前任务节点有自定义表单，则使用当前自定义表单
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if (ObjectUtil.isNotEmpty(task)) {
            if (ObjectUtil.isNotEmpty(task.getFormKey())) {
                formKey = task.getFormKey() + businessKey;
            }
        }
        //是否展示审批页签，当流程未结束，且申请人不是当前登录用户时，logicFlag 为true,其他为false
        boolean logicFlag = false;
        if (ObjectUtil.isNotEmpty(task)) {
            String currentUserId = String.valueOf(LoginContext.me().getAccountId());
            String applyUserId = taskService.getVariables(task.getId()).get(APPLY_USER_ID).toString();
            if (!currentUserId.equals(applyUserId)) {
                logicFlag = true;
            }
        }
        //获取流程图
        String taskImage = BASE64_PREFIX + this.getTaskImage(processInstanceId);
        resultMap.put("formKey", formKey);
        resultMap.put("logicFlag", logicFlag);
        resultMap.put("processImg", taskImage);
        List<ApproveHistoryResult> approveHistoryList = approveHistoryService.getApproveHistoryResultList(processInstanceId);
        resultMap.put("approveHistoryList", approveHistoryList);
        return resultMap;
    }

    /**
     * 获取运行中流程图片
     *
     * @Author xuyuxiang
     * @Date 2019/11/1 10:08
     **/
    public String getTaskImage(String processInstanceId) {
        InputStream imageStream = this.generateStream(processInstanceId);
        String imageBase64 = "";
        if (imageStream != null) {
            imageBase64 = Base64.encode(imageStream);
        }
        return imageBase64;
    }

    /**
     * 生成图片流
     *
     * @Author xuyuxiang
     * @Date 2019/11/1 10:08
     **/
    public InputStream generateStream(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        HistoricProcessInstance historicProcessInstance =
                historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        String processDefinitionId = null;
        //已执行节点集合
        List<String> executedActivityIdList = new ArrayList<>();
        List<HistoricActivityInstance> historicActivityInstanceList = new ArrayList<>();
        if (processInstance != null) {
            processDefinitionId = processInstance.getProcessDefinitionId();
        }
        if (historicProcessInstance != null) {
            processDefinitionId = historicProcessInstance.getProcessDefinitionId();
            historicActivityInstanceList =
                    historyService.createHistoricActivityInstanceQuery().
                            processInstanceId(processInstanceId).
                            orderByHistoricActivityInstanceId().asc().list();
            //遍历历史节点，将节点集合放入已执行节点
            for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
                String instanceActivityId = activityInstance.getActivityId();
                executedActivityIdList.add(instanceActivityId);
            }
        }
        if (ObjectUtil.isEmpty(processDefinitionId) || ObjectUtil.isEmpty(executedActivityIdList)) {
            return null;
        }

        //高亮线路id集合
        ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);
        List<String> highLightedFlows = this.getHighLightedFlows(definitionEntity, historicActivityInstanceList);
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        Context.setProcessEngineConfiguration((ProcessEngineConfigurationImpl) processEngineConfiguration);
        ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
        String activityFontName = processEngineConfiguration.getActivityFontName();
        String labelFontName = processEngineConfiguration.getLabelFontName();
        InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel, "png", executedActivityIdList, highLightedFlows, activityFontName, labelFontName, null, null, 1.0);
        return imageStream;
    }

    /**
     * 高亮线路
     *
     * @Author xuyuxiang
     * @Date 2019/11/1 10:09
     **/
    private List<String> getHighLightedFlows(ProcessDefinitionEntity processDefinitionEntity, List<HistoricActivityInstance> historicActivityInstances) {
        List<String> flowIdList = new ArrayList<>();// 用以保存高亮的线flowId
        for (int i = 0; i < historicActivityInstances.size() - 1; i++) {// 对历史流程节点进行遍历
            ActivityImpl activityImpl = processDefinitionEntity
                    .findActivity(historicActivityInstances.get(i)
                            .getActivityId());// 得到节点定义的详细信息
            List<ActivityImpl> sameStartTimeNodes = new ArrayList<ActivityImpl>();// 用以保存后需开始时间相同的节点
            ActivityImpl sameActivityImpl1 = processDefinitionEntity
                    .findActivity(historicActivityInstances.get(i + 1)
                            .getActivityId());
            // 将后面第一个节点放在时间相同节点的集合里
            sameStartTimeNodes.add(sameActivityImpl1);
            for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
                HistoricActivityInstance activityImpl1 = historicActivityInstances
                        .get(j);// 后续第一个节点
                HistoricActivityInstance activityImpl2 = historicActivityInstances
                        .get(j + 1);// 后续第二个节点
                if (activityImpl1.getStartTime().equals(
                        activityImpl2.getStartTime())) {
                    // 如果第一个节点和第二个节点开始时间相同保存
                    ActivityImpl sameActivityImpl2 = processDefinitionEntity
                            .findActivity(activityImpl2.getActivityId());
                    sameStartTimeNodes.add(sameActivityImpl2);
                } else {
                    // 有不相同跳出循环
                    break;
                }
            }
            List<PvmTransition> pvmTransitions = activityImpl
                    .getOutgoingTransitions();// 取出节点的所有出去的线
            for (PvmTransition pvmTransition : pvmTransitions) {
                // 对所有的线进行遍历
                ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition
                        .getDestination();
                // 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
                if (sameStartTimeNodes.contains(pvmActivityImpl)) {
                    flowIdList.add(pvmTransition.getId());
                }
            }
        }
        return flowIdList;
    }

    /**
     * 任务候选人列表
     *
     * @Author xuyuxiang
     * @Date 2019/11/14 15:28
     **/
    @Override
    public PageResult candidateList(String processInstanceId) {

        Page pageContext = this.getPageContext();
        //获取任务，此时任务一定处于运行中
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();

        //当前节点任务定义键值
        String taskDefinitionKey = task.getTaskDefinitionKey();

        //根据processInstanceId查询候选人集合，候选组（角色id）集合
        HistoricProcessInstance historicProcessInstance = historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();

        String processDefinitionId = historicProcessInstance.getProcessDefinitionId();

        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);

        ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) processDefinition;

        //查询到当前节点的定义
        ActivityImpl entityActivity = processDefinitionEntity.findActivity(taskDefinitionKey);

        //获取任务定义
        TaskDefinition taskDefinition = (TaskDefinition) entityActivity.getProperties().get("taskDefinition");

        //候选人
        Set<Expression> candidateUserIdExpressions = taskDefinition.getCandidateUserIdExpressions();

        //候选组
        Set<Expression> candidateGroupIdExpressions = taskDefinition.getCandidateGroupIdExpressions();

        //将用户id放到集合
        Set<Long> candidateUserIdSet = new HashSet<>();
        for (Expression expression : candidateUserIdExpressions) {
            //用户id
            Long candidateUserId = Long.valueOf(expression.getExpressionText());
            candidateUserIdSet.add(candidateUserId);
        }
        //将角色id放到组合
        Set<Long> candidateGroupIdSet = new HashSet<>();
        for (Expression expression : candidateGroupIdExpressions) {
            //组id(角色id)
            Long candidateGroupId = Long.valueOf(expression.getExpressionText());
            candidateGroupIdSet.add(candidateGroupId);
        }
        List<UserInfo> candidateUserList = actBaseService.getCandidateUser(candidateUserIdSet, candidateGroupIdSet);

        long count = candidateUserList.size();
        pageContext.setRecords(candidateUserList);
        pageContext.setTotal(count);
        return new PageResult(pageContext);
    }

    /**
     * 获取pageContext
     *
     * @Author xuyuxiang
     * @Date 2019/10/25 16:04
     **/
    private Page getPageContext() {
        return PageFactory.defaultPage();
    }
}
